package com.um.springboot.starter.service.impl;


import cn.hutool.core.util.StrUtil;
import com.um.springboot.starter.code.CommonCode;
import com.um.springboot.starter.entity.MultipartFileParam;
import com.um.springboot.starter.orm.entity.Response;
import com.um.springboot.starter.properties.HshProperties;
import com.um.springboot.starter.service.FileService;
import com.um.springboot.starter.utils.*;
import lombok.extern.slf4j.Slf4j;
import org.jdom2.Document;
import org.jdom2.Element;
import org.jdom2.input.SAXBuilder;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.util.ResourceUtils;

import java.io.*;
import java.util.*;

/**
 * @author 邓南辉
 * @date 2019/12/19
 * @copyright 广州优匠科技有限公司
 */
@Slf4j
@Service
public class FileServiceImpl implements FileService {

    @Autowired
    HshProperties hshProperties;

    /**
     * 上传任意文件
     * @param param
     * @return
     */
    @Override
    public Response uploadAnyFile(MultipartFileParam param) {
        return uploadAnyFile(param, null);
    }

    /**
     * 上传任意文件（只内部调用）
     * @param param
     * @param subPath
     * @return
     */
    @Override
    public Response uploadAnyFile(MultipartFileParam param, String subPath) {
        String fileType = param.getFileType().toLowerCase();
        List<String> allowFileTypes = StrUtil.split(hshProperties.getFile().getAllowFileType(), ',');
        if(!allowFileTypes.contains(fileType)){
            return Response.failed("不允许上传的文件类型！");
        }

        String name = MD5Util.MD5(param.getFileId())+"."+fileType;
        String uploadFilePath = "";
        try{
            if(hshProperties.getConfig().getPackaging().equals("jar") || !hshProperties.getFile().isRelative()){
                uploadFilePath = hshProperties.getFile().getPath();
            }else {
                uploadFilePath = ResourceUtils.getURL("classpath:").getPath() + "static" + hshProperties.getFile().getPath();

            }
            log.info("uploadPath:"+uploadFilePath);
        }catch (Exception ex){
            log.error("", ex);

            return Response.failed("路径配置错误！");

        }

        //添加日期路径
        String path = DateUtil.getDateString(new Date(), "yyyy-MM");
        //添加子路径
        if (StrUtil.isNotBlank(subPath)) {
            path = subPath + "/" + path;
        }
        String fullPath = uploadFilePath + "/" + path;

        //在磁盘中创建目录
        if (makeDir(fullPath))
        {
            //start == 0为第一次上传，如果文件存在，则需要删除
            if(param.getStart() == null || param.getStart().longValue() == 0){
                File myPath = new File(fullPath, name);
                if (myPath.exists()){
                    myPath.delete();
                }
            }

            //创建新文件
            if(!createFile(fullPath, name)){
                return Response.failed(CommonCode.CreateFileError);
            }

        }else{
            return Response.failed(CommonCode.CreateFolderError);
        }

        //写入文件
        try{
            InputStream is = param.getFile().getInputStream();
            byte[] data = new byte[1024];
            int len = 0;
            while ((len = is.read(data)) != -1) {
                writeFile(fullPath,name,data,len);
            }
        }catch(Exception ex){
            log.error("", ex);
            log.error("上传文件：写入文件出错:"+ex.getMessage());
            return Response.failed(CommonCode.WriteFileError);
        }

        if("false".equals(param.getIsComplete())){
            return Response.success();
        }else{
            Map data = new Hashtable();

            //不用加上hshProperties.getFile().getPath()。配置默认为"/upload"，访问路径前缀为"ip+端口+/upload"。这样上传到项目内和项目外才好保持一致
            data.put("relPath","/"+path + "/" + name);

            //data.put("absPath", fullPath+"/"+name);
            return Response.success(data);
        }
    }

    /**
     * 上传图片
     * @param param
     * @return
     */
    @Override
    public Response uploadImage(MultipartFileParam param){
        String path = param.getPath();//文件保存路径

        Response jsonObject = this.uploadAnyFile(param);
        if("false".equals(param.getIsComplete())){
            return jsonObject;
        }
        Map data = (Map)jsonObject.getData();
        String absPath = StringUtil.getTrim(data.get("absPath"));
        log.info("path:"+absPath);
        String inputFileName = FileUtil.getFileName(absPath);
        String inputDir = absPath.replaceAll("/"+inputFileName, "");
        List lstThumb = new ArrayList();
        log.info("path:"+StringUtil.getTrim(param.getThumbs()));
        log.info("inputDir:"+inputDir);

        if(param.getThumbs() != null) {
            String[] thumbs = StringUtil.getTrim(param.getThumbs()).split(",");
            Map mapThumb = new Hashtable();
            for (int index = 0; index < thumbs.length; index++) {
                String[] size = StringUtil.getTrim(thumbs[index]).split("x");
                if (size.length != 2) {
                    return Response.failed(CommonCode.CreateThumbError);
                }
                int width = 0;
                int height = 0;

                try {
                    width = Integer.parseInt(StringUtil.getTrim(size[0]));
                    height = Integer.parseInt(StringUtil.getTrim(size[1]));
                } catch (Exception ex) {
                    return Response.failed(CommonCode.CreateThumbError);
                }

                ImageUtil imageUtil = new ImageUtil();
                String outputFileName = width + "x" + height + "_" + inputFileName;
                String result = imageUtil.compressPic(inputDir, inputDir, inputFileName, outputFileName, width, height, false);
                log.info("result:" + result);

                if (path.equals("")) {
                    mapThumb.put(width + "x" + height, outputFileName);
                } else {
                    mapThumb.put(width + "x" + height, path + "/" + outputFileName);
                }

            }
            data.put("thumbs", mapThumb);
        }else{
            data.put("thumbs", new Hashtable<>());
        }
        return jsonObject;
    }

    /**
     * 上传scorm课件包
     * @param param
     * @return
     */
    @Override
    public Response uploadScorm(MultipartFileParam param){

        Response jsonObject = this.uploadAnyFile(param);
        if("false".equals(param.getIsComplete())){
            return jsonObject;
        }
        Map data = (Map)jsonObject.getData();
        String absPath = StringUtil.getTrim(data.get("absPath"));
        String relPath = StringUtil.getTrim(data.get("relPath"));
        int index = absPath.lastIndexOf(".");
        String zipFolder = absPath.substring(0, index);
        index = relPath.lastIndexOf(".");
        String relFolder = relPath.substring(0, index);
        FileUtil.unzip(absPath,zipFolder);
        FileUtil.deleteFile(absPath);

        Map mapResult = FileUtil.readXMLFile(zipFolder+"/imsmanifest.xml");
        if(mapResult.containsKey("msg")){
            jsonObject.setMsg(StringUtil.getTrim(mapResult.get("msg")));
            jsonObject.setCode(CommonCode.ReadImsmanifestError.getCode());
            return jsonObject;
        }
        String xmlContent = StringUtil.getTrim(mapResult.get("data"));
        String playUrl = "";
        try{
            InputStream in = new ByteArrayInputStream(xmlContent.getBytes("UTF-8"));
            SAXBuilder builder = new SAXBuilder();
            Document doc = builder.build(in);
            Element root = doc.getRootElement();
            List list = root.getChildren();
            Iterator it = list.iterator();
            while (it.hasNext()) {
                Element e = (Element) it.next();
                String k = e.getName();
                if("resources".equals(k)){
                    List children = e.getChildren();
                    playUrl = ((Element)children.get(0)).getAttributeValue("href");
                    break;
                }
            }
            if(playUrl == ""){
                jsonObject.setMsg("解析imsmanifest.xml文件失败，课件播放地址为空!");
                jsonObject.setCode(CommonCode.ReadImsmanifestError.getCode());
                return jsonObject;
            }

            data.put("relPath", relFolder+"/"+playUrl);
            data.put("absPath", relFolder+"/"+playUrl);
            jsonObject.setCode(CommonCode.Success.getCode());
            jsonObject.setData(data);
        }catch(Exception ex){
            jsonObject.setMsg("解析imsmanifest.xml文件失败，请检查imsmanifest.xml的格式是否正确!");
            jsonObject.setCode(CommonCode.ReadImsmanifestError.getCode());
            return jsonObject;
        }
        return jsonObject;
    }

    /**
     * 上传h5课件包
     * @param param
     * @return
     */
    @Override
    public Response uploadH5Zip(MultipartFileParam param){

        Response jsonObject = this.uploadAnyFile(param);
        if("false".equals(param.getIsComplete())){
            return jsonObject;
        }
        Map data = (Map)jsonObject.getData();
        String absPath = StringUtil.getTrim(data.get("absPath"));
        String relPath = StringUtil.getTrim(data.get("relPath"));
        int index = absPath.lastIndexOf(".");
        String zipFolder = absPath.substring(0, index);
        index = relPath.lastIndexOf(".");
        String relFolder = relPath.substring(0, index);
        FileUtil.unzip(absPath,zipFolder);
        FileUtil.deleteFile(absPath);

        data.put("relPath", relFolder+"/index.html");
        data.put("absPath", relFolder+"/index.html");
        jsonObject.setCode(CommonCode.Success.getCode());
        jsonObject.setData(data);
        return jsonObject;
    }

    private long writeFile(String path,String fileName,byte[] array,int len)throws Exception{
        String savePath = path+"/"+fileName;

        if(!FileUtil.exists(path))
            FileUtil.mkdirs(path);
        if (!FileUtil.exists(savePath))
        {
            OutputStream os = new FileOutputStream(savePath);
            //获得字节数组
            byte[] data = array;
            //开始写入
            os.write(data, 0, len);
            //清空缓冲区、关闭流
            os.flush();
            os.close();
        }else{
            OutputStream os = new FileOutputStream(savePath,true);
            //获得字节数组
            byte[] data = array;
            //开始写入
            os.write(data, 0, len);
            //清空缓冲区、关闭流
            os.flush();
            os.close();
        }

        File file = new File(savePath);
        return file.length();


    }

    /**
     * 如果文件不存在，则创建一个新文件
     * @param path 文件的路径
     * @param fileName 文件名称
     * @return 文件存在或创建成功返回true
     * @throws IOException
     */
    private boolean createFile(String path, String fileName)
    {
        boolean creator = true;
        File myPath = new File(path, fileName);
        if (!myPath.exists())
        {
            try{
                creator = myPath.createNewFile();
            }catch(Exception ex){
                creator = false;
                log.error("上传文件：创建文件出错:"+ex.getMessage());
            }
        }
        return creator;

    }

    /**
     * 如果目录不存在，则创建目录
     * @param path 需要创建的目录
     * @return 存在或创建成功返回true
     */
    private boolean makeDir(String path)
    {
        boolean mk = true;
        File myPath = new File(path);
        if (!myPath.exists())
        {
            mk = myPath.mkdirs();
        }
        return mk;
    }
}
